/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2022 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "dma.h"
#include "fatfs.h"
#include "i2c.h"
#include "ltdc.h"
#include "sai.h"
#include "sdmmc.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
#include "fmc.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "lvgl.h"
#include "lv_port_disp_template.h"
#include "lv_port_indev_template.h"
#include "ui.h"
// audio output
#include <stdio.h>
#include <math.h>
#include "stm32746g_discovery_audio.h"
#include "stm32746g_discovery_sdram.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void PeriphCommonClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

// display buffer: define in SDRAM
uint16_t lcd_disp_buf[480*272]  __attribute__((at(0XC0000000)));

// audio input
#define BUFSIZE 128 // basic unit of buffer
#define FILENAMELENGTH 13
static int16_t audioInputBuffer[BUFSIZE]; // double-vocal-trait data
uint16_t audioInputValue = 0; // the volume of audioInput(ADC transfer and GetValue)
uint16_t globalAudioInputFlag = 1; // AUDIO input func flag

uint8_t musicBuffer[BUFSIZE * 2]; // musicBuffer(read from SD)
uint32_t bytesread; // bytesred: Pointer to number of bytes read (f_read)

uint8_t addArray[BUFSIZE * 2]; // the mixin result of audioInput & music

uint16_t musicCount = 0; // total Count of Music
char activeMusicName[FILENAMELENGTH] = ""; // current selected music name
uint8_t activeMusicIndex = 0; // set default active music index to 0

// GPIO interrupt trigger
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
	if(GPIO_Pin == GPIO_PIN_11) {
		// toggle globalAudioInputFlag
		globalAudioInputFlag = globalAudioInputFlag ? 0 : 1;
	}
}

// ADC convert complete callback
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
	// convert complete, get value
	audioInputValue = (audioInputValue * 9 + HAL_ADC_GetValue(&hadc1)) / 10;
	
	// TIM3 PWM compare output
	if(globalAudioInputFlag) {
		// func enabled
		// Modify brightness based on sound
		// "/45": experience
		__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, audioInputValue / 30);
	} else {
		// func disabled
		// set brightness to 0
		__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 0);
	}
	
	// printf("convComplete: %d\n", audioInputValue);
}

void BSP_AUDIO_OUT_TransferComplete_CallBack(void) {
	// When the music output is full (256)
	// read music and fill in musicBuffer 128~256
  f_read(&SDFile, &musicBuffer[BUFSIZE], BUFSIZE, (void*)&bytesread);
	// mix musicBuffer[128~256] & audioInputBuffer[0~128]
	for(int i = BUFSIZE; i < BUFSIZE * 2; i++) {
	  addArray[i] = musicBuffer[i] + audioInputBuffer[i - BUFSIZE];
	}
	// printf("Audio OUT 256 Transfer OK\n");
}

void BSP_AUDIO_OUT_HalfTransfer_CallBack(void) {
	// When the music output is halfway (128)
	// read music and fill in musicBuffer 0~128
  f_read(&SDFile, &musicBuffer[0], BUFSIZE, (void*)&bytesread);
	// mix musicBuffer[0~128] & audioInputBuffer[0~128]
	for(int i = 0; i < BUFSIZE; i++){
	  addArray[i] = audioInputBuffer[i] + musicBuffer[i];
	}
	// printf("Audio OUT 128 Transfer OK\n");
}

void calcFileCountOfDir(char* dir_path, uint16_t* file_count) {
  uint16_t count = 0;
  FRESULT ret;    // operate result
  FATFS fs;       // FatFs object
  DIR dp;
  FIL fp;
  FILINFO Fileinfo;

  ret = f_opendir(&dp, dir_path);
	
  // no Directory
  if (ret == FR_NO_PATH) {
    printf("no Directory: %s\n", dir_path);
    while (1);
  } else if (ret != FR_OK) {
    printf("open %s failed\n", dir_path);
  } else {
    // success open Directory
    while (1) {
      ret = f_readdir(&dp, &Fileinfo);
      if (ret != FR_OK || Fileinfo.fname[0] == 0) {
        break;		// read error | read finished
      } else if (Fileinfo.fname[0] == '.') {
				// hidefile
        continue;
      } else if (Fileinfo.fattrib & AM_DIR) {
        // is Directory
        printf("isDirectory\n");
        continue;	// to next
      } else {
				if (Fileinfo.fname[0] != 0) {
					// note: default fname is short-file-name, fname[13]
					//       enable _USE_LFN in ffconf.h and get long-file-name
					// add filename to dropDownList
					lv_dropdown_add_option(ui_Dropdown1, Fileinfo.fname, 0);
					count++;
					printf("%s\n", Fileinfo.fname);	// print filename
        }
      }
    }
  }

  *file_count = count;
  f_closedir(&dp);	// close dir after open it
}

void readMusicViaName(char* musicName) {
	// f_open: open or create file, from ff.h file
	// SDFile: File object for SD, from fatfs.h file
	retSD = f_open(&SDFile, musicName, FA_OPEN_EXISTING | FA_READ);
	
	if(retSD == FR_OK) {
		printf("File Opened\n");
		// f_lseek: Move file pointer of the file object
		// cause .wav head info: 44bit
		f_lseek(&SDFile, 44);
		
		// f_read: Read data from the file
		// @param: SDFile: Pointer to the file object, point to the total read progress (always float on the top)
		// @param: musicBuffer: Pointer to data buffer, data receive target buffer.
		// @param: bufferSize: Number of bytes to read, single time call of f_read, how much data it read
		// @param: bytesred: Pointer to number of bytes read, will be reset to 0 after next call of f_read
		retSD = f_read(&SDFile, &musicBuffer[0], BUFSIZE * 2, (void*)&bytesread);
		
		if(retSD == FR_OK) {
			printf("File Readed\n");
		}
		
		// dont forget close file after writein
		// when switch next music, this func is called in ui_events.c
		// retSD = f_close(&SDFile);
		
		// after read, transfer musicBuffer to addArray
		for(int i = 0; i < BUFSIZE; i++){
			addArray[i] = musicBuffer[i];
		}
		
		// after transfer, output the addArray
		// mix-in step is executed in callback func defined above
		BSP_AUDIO_OUT_Play((uint16_t*)addArray, BUFSIZE);

	}
}

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

/* Configure the peripherals common clocks */
  PeriphCommonClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_LTDC_Init();
  MX_DMA_Init();
  MX_SAI2_Init();
  MX_I2C3_Init();
  MX_TIM2_Init();
  MX_USART1_UART_Init();
  MX_TIM3_Init();
  MX_ADC1_Init();
  MX_SDMMC1_SD_Init();
  MX_FATFS_Init();
  MX_FMC_Init();
  /* USER CODE BEGIN 2 */
	lv_init();
	lv_port_disp_init();
	lv_port_indev_init();
	ui_init();
	
	// audio input&output
	// interface init
	// SAI_AUDIO_FREQUENCY_48K: [AudioFreq]: Audio frequency to be configured for the SAI peripheral.
	// our music's sample rate is 44.1KHz
  BSP_AUDIO_IN_OUT_Init(INPUT_DEVICE_DIGITAL_MICROPHONE_2, OUTPUT_DEVICE_HEADPHONE, SAI_AUDIO_FREQUENCY_44K, DEFAULT_AUDIO_IN_BIT_RESOLUTION, DEFAULT_AUDIO_IN_CHANNEL_NBR);
	// audio input
	BSP_AUDIO_IN_Record((uint16_t*)audioInputBuffer, BUFSIZE);
	// choose CN10 interface output
	BSP_AUDIO_OUT_SetAudioFrameSlot(CODEC_AUDIOFRAME_SLOT_02);
	BSP_AUDIO_OUT_SetVolume(50); // set output volume
	// choose output buffer size: byte
	// BSP_AUDIO_OUT_Play((uint16_t*)audioInputBuffer, BUFSIZE*2);
	
	// TIM3 PWM: LED compare
	HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
	
	// ADC1 interrupt init
	HAL_ADC_Start_IT(&hadc1);
	
	// TIM2 PWM: ADC trigger
	HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
	
	// SDRAM init
	BSP_SDRAM_Init();
	
	// FileSystem related
	// retSD: File function return code (FRESULT), from fatfs.h file
	// f_mount: mount filesystem, from ff.h file
	// SDFatFS: File system object for SD logical drive, from fatfs.h file
	retSD = f_mount(&SDFatFS, "", 0);
	
	if(retSD == FR_OK) {
		printf("FileSystem Mounted\n");
		
		// get all files from SD and update them to dropDownList
		calcFileCountOfDir(SDPath, &musicCount);
		
		// set selected item
		lv_dropdown_set_selected(ui_Dropdown1, activeMusicIndex);
		
		// update active music name
		lv_dropdown_get_selected_str(ui_Dropdown1, activeMusicName, FILENAMELENGTH);
		
		// default music play: first music of list
		readMusicViaName(activeMusicName);
		
		// default: pause
		BSP_AUDIO_OUT_Pause();
		
	}

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		
		lv_task_handler();
		
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 25;
  RCC_OscInitStruct.PLL.PLLN = 432;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Activate the Over-Drive mode
  */
  if (HAL_PWREx_EnableOverDrive() != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief Peripherals Common Clock Configuration
  * @retval None
  */
void PeriphCommonClock_Config(void)
{
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

  /** Initializes the peripherals clock
  */
  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SAI2;
  PeriphClkInitStruct.PLLI2S.PLLI2SN = 344;
  PeriphClkInitStruct.PLLI2S.PLLI2SP = RCC_PLLP_DIV2;
  PeriphClkInitStruct.PLLI2S.PLLI2SR = 2;
  PeriphClkInitStruct.PLLI2S.PLLI2SQ = 7;
  PeriphClkInitStruct.PLLI2SDivQ = 1;
  PeriphClkInitStruct.Sai2ClockSelection = RCC_SAI2CLKSOURCE_PLLI2S;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

